﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using System.IO;
using System.Xml;
using Microsoft.Win32;
using EnvDTE;
using EnvDTE80;

namespace SPVisualDev
{
    /* 
     * Used togheter with SPDevExtensions to generate entity classes.
    */
    public class WSSTypeImporter
    {
        public Dictionary<Guid, string> WSSFieldsInAssembly { get; set; }
        public EnvDTE80.FileCodeModel2 CodeFile { get; set; }

        public void MakeEntityClassesFromFeatureDef(string FeatureFolderPath, string Namespace, bool isNewFile)
        {
            if (CodeFile == null)
                throw new Exception("No code file specified.");


            AddCodeImport("System");
            AddCodeImport("System.Collections.Generic");
            AddCodeImport("System.Text");
            AddCodeImport("SPDevExtensions.WSSTES");
            AddCodeImport("Microsoft.SharePoint");

            CodeNamespace cdNameSpace = null;
            foreach (CodeElement element in CodeFile.CodeElements)
            {
                if (element.Kind == vsCMElement.vsCMElementNamespace)
                {
                    if (isNewFile)
                        element.Name = Namespace;

                    cdNameSpace = element as CodeNamespace;
                    break;
                }
            }

            if (cdNameSpace == null)
                cdNameSpace = CodeFile.AddNamespace(Namespace, null);

            if (isNewFile)
            {
                foreach (CodeElement element in cdNameSpace.Children)
                    cdNameSpace.Remove(element);
            }

            ProcessFeatureDefinitionFile(FeatureFolderPath, cdNameSpace);

            CodeFile.Parent.Save(Common.GetProjectItemPath(CodeFile.Parent));
        }

        private void ProcessFeatureDefinitionFile(string FeatureFolderPath, CodeNamespace cdNameSpace)
        {
            XmlDocument featuredefXml = new XmlDocument();
            if (FeatureFolderPath.EndsWith("\\"))
                featuredefXml.Load(FeatureFolderPath + "Feature.xml");
            else
                featuredefXml.Load(FeatureFolderPath + "\\Feature.xml");

            XmlNamespaceManager xns_manager = new XmlNamespaceManager(featuredefXml.NameTable);
            xns_manager.AddNamespace("ns", "http://schemas.microsoft.com/sharepoint/");

            XmlNodeList nlist = featuredefXml.SelectNodes("//ns:ElementManifest", xns_manager);
            foreach (XmlNode n in nlist)
            {
                XmlDocument elementFileXml = new XmlDocument();
                elementFileXml.Load(FeatureFolderPath + "\\" + n.Attributes["Location"].Value);

                ProcessElementFile(elementFileXml, FeatureFolderPath, featuredefXml.DocumentElement.Attributes["Id"].Value, cdNameSpace);
            }

        }

        private void ProcessElementFile(XmlDocument elementFile, string FeatureFolderPath, string FeatureId, CodeNamespace cdNameSpace)
        {
            foreach (XmlNode node in elementFile.DocumentElement.ChildNodes)
            {
                if (node.NodeType != XmlNodeType.Element)
                    continue;

                XmlAttributeCollection acoll = node.Attributes;

                if (node.Name == "Field")
                {
                    string id = GetElementDefinitionAttribute(acoll, "ID");
                    if (string.IsNullOrEmpty(id))
                        id = GetElementDefinitionAttribute(acoll, "Id");

                    string internal_name = GetElementDefinitionAttribute(acoll, "Name");

                    /*
                    if (existingElements != null)
                    {
                        if (existingElements.Contains(internal_name))
                            continue;
                    }
                    */

                    string fieldtype = GetElementDefinitionAttribute(acoll, "Type");

                    if (fieldtype == "LookupMulti")
                        fieldtype = "Lookup";

                    string primitivetype;
                    if (fieldtype == "Counter" || fieldtype == "Integer")
                        primitivetype = "int?";
                    else if (fieldtype == "Number")
                        primitivetype = "double?";
                    else if (fieldtype == "Boolean")
                        primitivetype = "bool?";
                    else if (fieldtype == "DateTime")
                        primitivetype = "DateTime";
                    else
                        primitivetype = "string";

                    string fieldtype_str;
                    try
                    {
                        SPFieldType ftype = (SPFieldType)Enum.Parse(typeof(SPFieldType), fieldtype);
                        fieldtype_str = "FieldType = SPFieldType." + ftype.ToString();
                    }
                    catch
                    {
                        fieldtype_str = "CustomFieldType = \"" + fieldtype.ToString() + "\"";
                    }

                    string classname = GetElementDefinitionAttribute(acoll, "Name");
                    string basestring = "WSSField<" + classname + ", " + primitivetype + ">";

                    CodeClass cdClass = AddFieldClass(cdNameSpace as CodeElement, new Guid(FeatureId), new Guid(id), classname, basestring);
                }
                else if (node.Name == "ListInstance")
                {
                    string listtitle = GetElementDefinitionAttribute(acoll, "Title");
                    string listurl = GetElementDefinitionAttribute(acoll, "Url");
                    string listtype = GetElementDefinitionAttribute(acoll, "TemplateType");

                    string classname = listurl.Replace(" ", "");
                    if (listurl.LastIndexOf("/") == -1)
                        classname = listurl;
                    else
                        classname = listurl.Substring(listurl.LastIndexOf("/") + 1);

                    CodeClass cdClass = AddListInstanceClass(cdNameSpace as CodeElement, new Guid(FeatureId), listurl, classname, "WSSListInstance<" + classname + ">");

                    ProcessListSchemaXML(cdClass, GetElementDefinitionAttribute(acoll, "FeatureId"), listtype);

                }
                else if (node.Name == "ContentType")
                {
                    string name = GetElementDefinitionAttribute(acoll, "Name");
                    string ctid = GetElementDefinitionAttribute(acoll, "ID");
                    string classname = name.Replace("$Resources:","").Replace(" ", "");

                    CodeClass cdClass = AddContentTypeClass(cdNameSpace as CodeElement, new Guid(FeatureId), new SPContentTypeId(ctid), classname, "WSSContentType<" + classname + ">");
                    ProcessContentTypeSchema(cdClass, node);
                }
                else if (node.Name == "ListTemplate")
                {
                    string listname = GetElementDefinitionAttribute(acoll, "Name");
                    string listtype = GetElementDefinitionAttribute(acoll, "Type");

                    CodeClass cdClass = AddListTemplateClass(cdNameSpace as CodeElement, new Guid(FeatureId), listname, listname, "WSSListTemplate<" + listname + ">");
                    ProcessListSchemaXML(cdClass, FeatureId, listtype);
                }
            }
        }

        private void AddCodeImport(string ImportNamespace)
        {
            foreach (CodeElement element in CodeFile.CodeElements)
            {
                if (element.Kind == vsCMElement.vsCMElementImportStmt)
                {
                    CodeImport imp = element as CodeImport;
                    if (imp.Namespace == ImportNamespace)
                        return;
                }
            }

            CodeFile.AddImport(ImportNamespace, null, "");
        }

        private CodeClass AddContentTypeClass(CodeElement Parent, Guid FeatureId, SPContentTypeId ContentTypeID, string ClassName, string BaseString)
        {
            CodeClass cdClass;
            foreach (CodeElement element in Parent.Children)
            {
                if (element.Kind == vsCMElement.vsCMElementClass)
                {
                    try
                    {
                        Guid curr_feature_id = new Guid(((element as CodeClass).Attributes.Item("WSSFeatureAssociation").Children.Item("ID") as CodeAttributeArgument).Value.Replace("\"",""));
                        SPContentTypeId curr_ct_id = new SPContentTypeId(((element as CodeClass).Attributes.Item("WSSListInstanceAttributes").Children.Item("WebRelURL") as CodeAttributeArgument).Value.Replace("\"",""));

                        if (FeatureId == curr_feature_id && ContentTypeID == curr_ct_id)
                            return (CodeClass)element;
                    }
                    catch { }

                }
            }

            try
            {
                cdClass = Parent.Children.Item(ClassName) as CodeClass;
            }
            catch
            {
                if (Parent is CodeNamespace)
                    cdClass = (Parent as CodeNamespace).AddClass(ClassName, -1, BaseString, null, vsCMAccess.vsCMAccessPublic);
                else
                    cdClass = (Parent as CodeClass).AddClass(ClassName, -1, BaseString, null, vsCMAccess.vsCMAccessPublic);

                cdClass.AddAttribute("WSSFeatureAssociation", "ID = \"" + FeatureId.ToString() + "\"", 0);
                cdClass.AddAttribute("WSSContentTypeAttributes", "ID = \"" + ContentTypeID.ToString() + "\"", -1);
            }

            return cdClass;
        }

        private CodeClass AddListTemplateClass(CodeElement Parent, Guid FeatureId, string InternalName, string ClassName, string BaseString)
        {
            CodeClass cdClass;
            foreach (CodeElement element in Parent.Children)
            {
                if (element.Kind == vsCMElement.vsCMElementClass)
                {
                    try
                    {
                        Guid curr_feature_id = new Guid(((element as CodeClass).Attributes.Item("WSSFeatureAssociation").Children.Item("ID") as CodeAttributeArgument).Value.Replace("\"", ""));
                        string curr_int_name = ((element as CodeClass).Attributes.Item("WSSListTemplateAttributes").Children.Item("InternalName") as CodeAttributeArgument).Value.Replace("\"", "");

                        if (FeatureId == curr_feature_id && InternalName == curr_int_name)
                            return (CodeClass)element;
                    }
                    catch { }

                }
            }

            try
            {
                cdClass = Parent.Children.Item(ClassName) as CodeClass;
            }
            catch
            {
                if (Parent is CodeNamespace)
                    cdClass = (Parent as CodeNamespace).AddClass(ClassName, -1, BaseString, null, vsCMAccess.vsCMAccessPublic);
                else
                    cdClass = (Parent as CodeClass).AddClass(ClassName, -1, BaseString, null, vsCMAccess.vsCMAccessPublic);

                cdClass.AddAttribute("WSSFeatureAssociation", "ID = \"" + FeatureId.ToString() + "\"", 0);
                cdClass.AddAttribute("WSSListTemplateAttributes", "InternalName = \"" + InternalName + "\"", -1);
            }

            return cdClass;
        }

        private CodeClass AddListInstanceClass(CodeElement Parent, Guid FeatureId, string WebRelURL, string ClassName, string BaseString)
        {
            CodeClass cdClass;
            foreach (CodeElement element in Parent.Children)
            {
                if (element.Kind == vsCMElement.vsCMElementClass)
                {
                    try
                    {
                        Guid curr_feature_id = new Guid(((element as CodeClass).Attributes.Item("WSSFeatureAssociation").Children.Item("ID") as CodeAttributeArgument).Value.Replace("\"", ""));
                        string curr_web_rel_url = ((element as CodeClass).Attributes.Item("WSSListInstanceAttributes").Children.Item("WebRelURL") as CodeAttributeArgument).Value.Replace("\"", "");

                        if (FeatureId == curr_feature_id && WebRelURL.ToLower() == curr_web_rel_url.ToLower())
                            return (CodeClass)element;
                    }
                    catch { }

                }
            }

            try
            {
                cdClass = Parent.Children.Item(ClassName) as CodeClass;
            }
            catch
            {
                if (Parent is CodeNamespace)
                    cdClass = (Parent as CodeNamespace).AddClass(ClassName, -1, BaseString, null, vsCMAccess.vsCMAccessPublic);
                else
                    cdClass = (Parent as CodeClass).AddClass(ClassName, -1, BaseString, null, vsCMAccess.vsCMAccessPublic);

                cdClass.AddAttribute("WSSFeatureAssociation", "ID = \"" + FeatureId.ToString() + "\"", 0);
                cdClass.AddAttribute("WSSListInstanceAttributes", "WebRelURL = \"" + WebRelURL + "\"", -1);
            }

            return cdClass;
        }

        private CodeClass AddFieldClass(CodeElement Parent, Guid FeatureId, Guid FieldID, string ClassName, string BaseString)
        {
            CodeClass cdClass;
            foreach (CodeElement element in Parent.Children)
            {
                if (element.Kind == vsCMElement.vsCMElementClass)
                {
                    try
                    {
                        Guid curr_feature_id;
                        if (FeatureId == Guid.Empty)
                            curr_feature_id = Guid.Empty;
                        else
                            curr_feature_id = new Guid(((element as CodeClass).Attributes.Item("WSSFeatureAssociation").Children.Item("ID") as CodeAttributeArgument).Value.Replace("\"", ""));

                        Guid curr_field_id = new Guid(((element as CodeClass).Attributes.Item("WSSFieldAttributes").Children.Item("ID") as CodeAttributeArgument).Value.Replace("\"", ""));

                        if (FeatureId == curr_feature_id && FieldID == curr_field_id)
                            return (CodeClass)element;
                    }
                    catch { }
                    
                }
            }

            try
            {
                cdClass = Parent.Children.Item(ClassName) as CodeClass;
            }
            catch
            {
                CodeElement insert = FindLastPosition(Parent, vsCMElement.vsCMElementClass, "WSSFeatureAssociation");

                if (Parent is CodeNamespace)
                    cdClass = (Parent as CodeNamespace).AddClass(ClassName, -1, BaseString, null, vsCMAccess.vsCMAccessPublic);
                else
                    cdClass = (Parent as CodeClass).AddClass(ClassName, -1, BaseString, null, vsCMAccess.vsCMAccessPublic);

                if (FeatureId != Guid.Empty)
                    cdClass.AddAttribute("WSSFeatureAssociation", "ID = \"" + FeatureId.ToString() + "\"", 0);

                cdClass.AddAttribute("WSSFieldAttributes", "ID = \"" + FieldID.ToString() + "\"", -1);
            }

            return cdClass;
        }

        private CodeProperty AddFieldProperty(CodeElement cdElement, string FieldName, Guid FieldID, object type)
        {
            CodeProperty ret = null;
            try
            {
                ret = cdElement.Children.Item("Field" + FieldName) as CodeProperty;
            }
            catch { }

            if (ret == null)
            {
                foreach (CodeElement element in cdElement.Children)
                {
                    if (element.Kind == vsCMElement.vsCMElementProperty)
                    {
                        CodeProperty prop = element as CodeProperty;
                        try
                        {
                            Guid id = new Guid((prop.Attributes.Item("WSSFieldValueProperty").Children.Item("FieldID") as CodeAttributeArgument).Value.Replace("\"",""));
                            if (id == FieldID)
                            {
                                return prop;
                            }
                        }
                        catch { }
                    }
                }

                CodeElement insert = FindLastPosition(cdElement, vsCMElement.vsCMElementProperty, "WSSFieldValueProperty");

                if (cdElement is CodeClass)
                    ret = (cdElement as CodeClass).AddProperty(FieldName, FieldName, type, insert, vsCMAccess.vsCMAccessPublic, null);
                else
                    ret = (cdElement as CodeStruct).AddProperty(FieldName, FieldName, type, insert, vsCMAccess.vsCMAccessPublic, null);

                ret.AddAttribute("WSSFieldValueProperty", "FieldID = \"" + FieldID.ToString() + "\", InternalName = \"" + FieldName + "\"", -1);

                EditPoint point = ret.Getter.StartPoint.CreateEditPoint();
                point.Delete(ret.Getter.EndPoint);
                point.Insert("get;");

                point = ret.Setter.StartPoint.CreateEditPoint();
                point.Delete(ret.Setter.EndPoint);
                point.Insert("set;");

            }

            return ret;
        }

        private CodeAttribute AddCodeAttribute(CodeClass cdClass, string AttributeName, string Value)
        {
            CodeAttribute cdAttr;
            try
            {
                cdAttr = cdClass.Attributes.Item(AttributeName) as CodeAttribute;
            }
            catch
            {
                cdAttr = cdClass.AddAttribute(AttributeName, Value, -1);
            }

            return cdAttr;
        }

        private CodeAttributeArgument GetCodeAttributeArgument(CodeAttribute cdAttribute, string Name)
        {
            CodeAttributeArgument arg = null;
            try
            {
                arg = cdAttribute.Children.Item(Name) as CodeAttributeArgument;
            }
            catch { }

            return arg;
        }


        private static string GetElementDefinitionAttribute(XmlAttributeCollection Attributes, string Attribute)
        {
            if (Attributes.GetNamedItem(Attribute) != null)
                return Attributes[Attribute].Value;
            else
                return string.Empty;
        }

        private void ProcessContentTypeSchema(CodeClass ParentClass, XmlNode ContentTypeXmlNode)
        {
            List<AddedFieldsStruct> fields_to_add = new List<AddedFieldsStruct>();

            ParseFieldRefsInContentType(ContentTypeXmlNode, fields_to_add);

            try
            {
                CodeVariable variable = ParentClass.AddVariable("FieldValues", "FieldValuesStruct", -1, vsCMAccess.vsCMAccessPublic, null);
                variable.InitExpression = "new FieldValuesStruct()";
            }
            catch { }

            RenderFieldContent(ParentClass, fields_to_add, 0);
            RenderFieldContent(ParentClass, fields_to_add, 1);
        }

        private void ProcessListSchemaXML(CodeClass ParentClass, string FeatureId, string ListType)
        {
            string element_name = null;
            SPFeatureDefinition def = SPFarm.Local.FeatureDefinitions[new Guid(FeatureId)];
            foreach (SPElementDefinition element in def.GetElementDefinitions(System.Globalization.CultureInfo.InvariantCulture))
            {
                if (element.ElementType == "ListTemplate")
                {
                    if (GetElementDefinitionAttribute(element.XmlDefinition.Attributes, "Type") == ListType)
                    {
                        element_name = GetElementDefinitionAttribute(element.XmlDefinition.Attributes, "Name");
                        break;
                    }
                }
            }

            if (element_name == null)
                throw new Exception("List type '" + ListType + "' not found.");


            string schemaxml_path = def.RootDirectory + "\\" + element_name + "\\Schema.xml";
            if (!File.Exists(schemaxml_path))
                throw new Exception("Schema.xml file not found at " + schemaxml_path);

            XmlDocument xmldoc = new XmlDocument();
            xmldoc.PreserveWhitespace = false;
            xmldoc.Load(def.RootDirectory + "\\" + element_name + "\\Schema.xml");

            List<AddedFieldsStruct> fields_to_add = new List<AddedFieldsStruct>();

            if (xmldoc.DocumentElement.NamespaceURI == "http://schemas.microsoft.com/sharepoint/")
            {
                XmlNamespaceManager manager = new XmlNamespaceManager(xmldoc.NameTable);
                manager.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/");

                foreach (XmlNode n in xmldoc.SelectNodes("/sp:List/sp:MetaData/sp:Fields/sp:Field", manager))
                    fields_to_add.Add(new AddedFieldsStruct(true, GetFieldIdFromXml(n), n));
            }
            else
            {
                foreach (XmlNode n in xmldoc.SelectNodes("/List/MetaData/Fields/Field"))
                    fields_to_add.Add(new AddedFieldsStruct(true, GetFieldIdFromXml(n), n));
            }

            XmlNode xn_ct;
            if (xmldoc.DocumentElement.NamespaceURI == "http://schemas.microsoft.com/sharepoint/")
            {
                XmlNamespaceManager manager = new XmlNamespaceManager(xmldoc.NameTable);
                manager.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/");
                xn_ct = xmldoc.SelectSingleNode("/sp:List/sp:MetaData/sp:ContentTypes", manager);
            }
            else
            {
                xn_ct = xmldoc.SelectSingleNode("/List/MetaData/ContentTypes");
            }
            
            if (xn_ct != null)
            {
                foreach (XmlNode n in xn_ct.ChildNodes)
                {
                    if (n.Name == "ContentType" || n.Name == "ContentTypeRef")
                    {
                        //Own fieldrefs.
                        foreach (XmlNode n2 in n.SelectNodes("FieldRefs/FieldRef"))
                        {
                            XmlNode field_node = FindField(GetFieldIdFromXml(n2));
                            if (field_node == null)
                                continue;

                            if (n2.Name == "FieldRef")
                            {
                                fields_to_add.Add(new AddedFieldsStruct(true, GetFieldIdFromXml(field_node), field_node));
                            }
                            else if (n2.Name == "RemoveFieldRef")
                            {
                                fields_to_add.Add(new AddedFieldsStruct(false, GetFieldIdFromXml(field_node), field_node));
                            }
                        }

                        ParseFieldRefsInContentType(n, fields_to_add);
                    }
                }
            }

            try
            {
                CodeVariable variable = ParentClass.AddVariable("FieldValues", "FieldValuesStruct", -1, vsCMAccess.vsCMAccessPublic, null);
                variable.InitExpression = "new FieldValuesStruct()";
            }
            catch { }

            RenderFieldContent(ParentClass, fields_to_add, 0);
            RenderFieldContent(ParentClass, fields_to_add, 1);

            /*
            if (ParentClass.Members.Count > 0)
            {
                ParentClass.Members.Item(1).StartPoint.CreateEditPoint().Insert("#region Generated code area\r\n\r\n");
                ParentClass.Members.Item(ParentClass.Members.Count).EndPoint.CreateEditPoint().Insert("\r\n\r\n#endregion");
            }
            */
        }

        private void ParseFieldRefsInContentType(XmlNode ContentTypeXmlNode, IList<AddedFieldsStruct> AddedFields)
        {
            XmlElement el = ContentTypeXmlNode as XmlElement;


            //Fieldrefs from content type
            XmlNode ct = FindContentType(el.GetAttribute("ID"), false);
            if (ct != null)
            {
                XmlNamespaceManager manager = new XmlNamespaceManager(ct.OwnerDocument.NameTable);
                manager.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/");

                foreach (XmlNode n2 in ct.SelectNodes("sp:FieldRefs/*", manager))
                {
                    XmlNode field_node = FindField(GetFieldIdFromXml(n2));
                    if (field_node == null)
                        continue;

                    if (n2.Name == "FieldRef")
                    {
                        AddedFields.Add(new AddedFieldsStruct(true, GetFieldIdFromXml(field_node), field_node));
                    }
                    else if (n2.Name == "RemoveFieldRef")
                    {
                        AddedFields.Add(new AddedFieldsStruct(false, GetFieldIdFromXml(field_node), field_node));
                    }
                }


                //Find fields in parent content types.
                ct = FindContentType(el.GetAttribute("ID"), true);
                while (ct != null)
                {
                    manager = new XmlNamespaceManager(ct.OwnerDocument.NameTable);
                    manager.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/");

                    foreach (XmlNode n2 in ct.SelectNodes("sp:FieldRefs/sp:FieldRef", manager))
                    {
                        XmlNode field_node = FindField(GetFieldIdFromXml(n2));
                        if (field_node == null)
                            continue;

                        if (n2.Name == "FieldRef")
                        {
                            AddedFields.Add(new AddedFieldsStruct(true, GetFieldIdFromXml(field_node), field_node));
                        }
                        else if (n2.Name == "RemoveFieldRef")
                        {
                            AddedFields.Add(new AddedFieldsStruct(false, GetFieldIdFromXml(field_node), field_node));
                        }
                    }

                    ct = FindContentType(GetElementDefinitionAttribute(ct.Attributes, "ID"), true);
                }
            }
        }

        private void RenderFieldContent(CodeClass ParentClass, IList<AddedFieldsStruct> AddedFields, int MemberType)
        {
            //MemberType 0 = Render properties.
            //MemberType 1 = Render classes.

            Dictionary<Guid, XmlNode> fields_to_render = new Dictionary<Guid, XmlNode>();
            for (int i = AddedFields.Count - 1; i >= 0; i--)
            {
                AddedFieldsStruct s = AddedFields[i];
                if (s.AddField)
                {
                    if (!fields_to_render.ContainsKey(s.FieldID))
                        fields_to_render.Add(s.FieldID, s.FieldXml);
                }
                else
                {
                    if (fields_to_render.ContainsKey(s.FieldID))
                        fields_to_render.Remove(s.FieldID);
                }
            }

            if (MemberType == 0)
            {
                CodeStruct par = null;
                foreach (CodeElement element in ParentClass.Members)
                {
                    if (element.Kind == vsCMElement.vsCMElementStruct)
                    {
                        if (element.Name == "FieldValuesStruct")
                        {
                            par = element as CodeStruct;
                            break;
                        }
                    }
                }

                if (par == null)
                    par = ParentClass.AddStruct("FieldValuesStruct", -1, null, null, vsCMAccess.vsCMAccessPublic);

                foreach (KeyValuePair<Guid, XmlNode> kvp in fields_to_render)
                    ProcessFieldXml(par as CodeElement, kvp.Value as XmlElement, MemberType);
            }
            else
            {
                CodeClass par = null;
                foreach (CodeElement element in ParentClass.Members)
                {
                    if (element.Kind == vsCMElement.vsCMElementClass)
                    {
                        if (element.Name == "FieldDefinitions")
                        {
                            par = element as CodeClass;
                            break;
                        }
                    }
                }

                if (par == null)
                    par = ParentClass.AddClass("FieldDefinitions", -1, null, null, vsCMAccess.vsCMAccessPublic);

                foreach (KeyValuePair<Guid, XmlNode> kvp in fields_to_render)
                    ProcessFieldXml(par as CodeElement, kvp.Value as XmlElement, MemberType);

            }
        }

        private void ProcessFieldXml(CodeElement ParentElement, XmlElement FieldXml, int MemberType)
        {

            string id;
            if (!string.IsNullOrEmpty(FieldXml.GetAttribute("ID")))
                id = FieldXml.GetAttribute("ID");
            else
                id = FieldXml.GetAttribute("Id");


            string fieldtype = FieldXml.GetAttribute("Type");
            if (fieldtype == "LookupMulti")
                fieldtype = "Lookup";

            string primitivetype;
            if (fieldtype == "Counter" || fieldtype == "Integer")
                primitivetype = "int?";
            else if (fieldtype == "Number")
                primitivetype = "double?";
            else if (fieldtype == "Boolean")
                primitivetype = "bool?";
            else if (fieldtype == "DateTime")
                primitivetype = "DateTime";
            else
                primitivetype = "string";

            string name = FieldXml.GetAttribute("Name");

            if (MemberType == 0)
            {
                CodeProperty cdProperty = AddFieldProperty(ParentElement, name, new Guid(id), primitivetype);
            }
            else
            {
                if (!WSSFieldsInAssembly.ContainsKey(new Guid(id)))
                {
                    AddFieldClass(ParentElement, Guid.Empty, new Guid(id), name, "WSSField<" + name + ", " + primitivetype + ">");
                }
                else
                {
                    AddFieldClass(ParentElement, Guid.Empty, new Guid(id), name, WSSFieldsInAssembly[new Guid(id)]);
                }
            }
        }

        private EditPoint FindRegion(EditPoint StartPoint, string RegionName)
        {
            EditPoint point = null;
            TextRanges ranges = null;
            StartPoint.CreateEditPoint().FindPattern(RegionName, (int)vsFindOptions.vsFindOptionsMatchInHiddenText, ref point, ref ranges);

            return point;
        }

        private CodeElement FindLastPosition(CodeElement Parent, vsCMElement ElementKind, string AttributeName)
        {
            for (int i = Parent.Children.Count; i > 0; i-- )
            {
                CodeElement element = Parent.Children.Item(i) as CodeElement;
                if (element.Kind == ElementKind)
                {
                    CodeElements attributes;
                    if (element is CodeClass)
                        attributes = (element as CodeClass).Attributes;
                    else if (element is CodeProperty)
                        attributes = (element as CodeProperty).Attributes;
                    else
                        throw new Exception();

                    foreach (CodeElement el2 in attributes)
                    {
                        if ((el2 as CodeAttribute).Name == AttributeName)
                            return element;
                    }

                }

            }

            return null;
        }

        private static XmlNode FindContentType(string ContentTypeId, bool FindParentContentType)
        {
            if (ContentTypeId == "0x")
                return null;

            SPContentTypeId ct_to_find = new SPContentTypeId(ContentTypeId);
            foreach (SPFeatureDefinition def in SPFarm.Local.FeatureDefinitions)
            {
                foreach (SPElementDefinition element in def.GetElementDefinitions(System.Globalization.CultureInfo.InvariantCulture))
                {
                    if (element.ElementType == "ContentType")
                    {
                        SPContentTypeId ct_current_id = new SPContentTypeId(GetElementDefinitionAttribute(element.XmlDefinition.Attributes, "ID"));
                        if (FindParentContentType)
                        {
                            if (ct_current_id == ct_to_find.Parent)
                                return element.XmlDefinition;
                        }
                        else
                        {
                            if (ct_current_id == ct_to_find)
                                return element.XmlDefinition;
                        }
                    }
                }
            }

            return null;
        }

        private static XmlNode FindField(Guid FieldId)
        {
            foreach (SPFeatureDefinition def in SPFarm.Local.FeatureDefinitions)
            {
                foreach (SPElementDefinition element in def.GetElementDefinitions(System.Globalization.CultureInfo.InvariantCulture))
                {
                    if (element.ElementType == "Field")
                    {
                        Guid id = GetFieldIdFromXml(element.XmlDefinition);

                        if (id == FieldId)
                            return element.XmlDefinition;
                    }
                }
            }

            return null;
        }

        private static Guid GetFieldIdFromXml(XmlNode FieldXml)
        {
            string id = GetElementDefinitionAttribute(FieldXml.Attributes, "ID");
            if (string.IsNullOrEmpty(id))
                id = GetElementDefinitionAttribute(FieldXml.Attributes, "Id");

            return new Guid(id);
        }

        private struct AddedFieldsStruct
        {
            public bool AddField;
            public Guid FieldID;
            public XmlNode FieldXml;

            public AddedFieldsStruct(bool addField, Guid fieldId, XmlNode fieldXml)
            {
                AddField = addField;
                FieldID = fieldId;
                FieldXml = fieldXml;
            }
        }

    }

}
